﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using MultWebApi.AuthCenter.Auth;
using MultWebApi.Models;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace MultWebApi.AuthCenter.Controllers
{
    [ApiController]
    [UserCheckControllerFilter]
    [Route("Account/")]
    public class AccountController : Controller
    {
        private List<SysAccount> sysAccounts;
        private AuthJwtDto jwtDto;
        private IMemoryCache _cache;
        public AccountController(IMemoryCache cacheData,IOptions<AuthJwtDto> op)
        {
            jwtDto=op.Value;
            _cache=cacheData;
            sysAccounts =new List<SysAccount>();
            sysAccounts.Add(new SysAccount() { Account="admin", Name="管理员", Action="Inner", Password="456789",SysFlag=3});
            sysAccounts.Add(new SysAccount() { Account = "bdmin", Name = "销售主管", Action = "Inner", Password = "123456", SysFlag=1 });
            sysAccounts.Add(new SysAccount() { Account = "cdmin", Name = "生产车间主任", Action = "Inner", Password = "123456", SysFlag = 2 });
            sysAccounts.Add(new SysAccount() { Account = "guest", Name = "商家", Action = "Guest", Password = "1", SysFlag = 1 });
        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [AllowAnonymous]//AllowAnonymous属性以允许未经身份验证的用户到单个操作的访问
        [HttpPost("Login")]
        [UserCheck(SystemFlag = 0, Action = "Guest")]
        public JsonResult In([FromBody] ParamLogInModel model)
        {
            DataResult rst = new DataResult();
            rst.ErrorCode = SysErrorCode.KFail;
            try
            {

                #region 进入逻辑处理
                SysAccount loginAccount=new SysAccount();
                if (sysAccounts.Any(t => t.Account == model.Account))
                {
                    loginAccount=sysAccounts.FirstOrDefault(t=>t.Account == model.Account && t.Password==model.Password);
                    if (loginAccount == null)
                    {
                        rst.ErrorCode = SysErrorCode.PwdFormatError;
                    }
                    else
                    {
                        rst.ErrorCode = SysErrorCode.KSuccess;
                    }
                }
                else
                {
                    rst.ErrorCode = SysErrorCode.PwdFormatError;
                }
                if (rst.ErrorCode != SysErrorCode.KSuccess)
                {
                    goto TOEND;
                }
                #endregion

                
                SysAccountDto userDto = new SysAccountDto()
                {
                     Account = loginAccount.Account,
                     Action = loginAccount.Action,
                     Name = loginAccount.Name,
                      SysFlag= loginAccount.SysFlag
                };
                JwtAuthorizationDto jwtAuthorizationDto =CreateToken(userDto);
                if (jwtAuthorizationDto == null)
                {
                    rst.ErrorCode = SysErrorCode.KFail;
                    goto TOEND;
                }
                _cache.Remove("Uo@" + loginAccount.Account);
                _cache.Set("Ur@" + loginAccount.Account, jwtAuthorizationDto.Token, TimeSpan.FromHours(2));
                rst.Data = jwtAuthorizationDto;
            }
            catch (Exception ex)
            {
                rst.ErrorCode = SysErrorCode.KFail;
            }
            TOEND:
            rst.Message = SysErrorCode.GetErrorMessge(rst.ErrorCode);
            return new JsonResult(rst);
        }


        /// <summary>
        /// 登出
        /// </summary>
        /// <returns></returns>
        [HttpPost("Logout")]
        [UserCheck(SystemFlag = 0, Action = "Guest")]
        public JsonResult Out()
        {
            DataResult rst = new DataResult();
            rst.ErrorCode = SysErrorCode.KFail;
            try
            {
                var userInfo = GetUserJwtAuthInfo();
                if (userInfo != null)
                {
                    _cache.Set("Uo@" + userInfo.Account, userInfo.SysFlag, TimeSpan.FromHours(1));
                    //ILoginManager.LogOutByToken(GetCurrentToken());
                    rst.ErrorCode = SysErrorCode.KSuccess;
                }
            }
            catch (Exception ex)
            {

            }
            rst.Message = SysErrorCode.GetErrorMessge(rst.ErrorCode);
            return new JsonResult(rst);
        }

      
        [HttpGet("GetUserName")]  
        [UserCheck(SystemFlag = 0, Action = "Guest")]
        public JsonResult GetUserName()
        {
            string name = HttpContext.User.Claims.FirstOrDefault(p => p.Type == ClaimTypes.Name)?.Value;
            string userAccount = HttpContext.User.Claims.FirstOrDefault(p => p.Type == ClaimTypes.Sid)?.Value;
            var rst = new DataResult();
            if (_cache.TryGetValue<int>("Uo@" + userAccount, out int sysFlag))
            {
                rst.ErrorCode = SysErrorCode.KTokenInvalid;
            }
            else
            {
                rst.ErrorCode = SysErrorCode.KSuccess;
                rst.Data = new { Name = name };
                rst.Message = SysErrorCode.GetErrorMessge(rst.ErrorCode);
            }
            return new JsonResult(rst);
        }

        [UserCheck(SystemFlag = 0, Action = "Guest")]
        [HttpPost("RefreshToken")]
        public JsonResult RefreshToken()
        {
            DataResult rst = new DataResult();
            rst.ErrorCode = SysErrorCode.KFail;
            try
            {
                var userInfo = GetUserJwtAuthInfo();
                if (userInfo == null)
                {
                    rst.ErrorCode = SysErrorCode.KFail;
                    goto TOEND;
                }
                JwtAuthorizationDto jwtAuthorizationDto = CreateToken(userInfo);
                _cache.Set("Ur@" + userInfo.Account, jwtAuthorizationDto.Token, TimeSpan.FromHours(2));
                rst.Data = jwtAuthorizationDto;
                rst.ErrorCode = SysErrorCode.KSuccess;
            }
            catch (Exception ex)
            {
            }
            TOEND:
            rst.Message = SysErrorCode.GetErrorMessge(rst.ErrorCode);
            return new JsonResult(rst);
        }

        [AllowAnonymous]
        [HttpPost("Check")]
        public JsonResult CheckToken(string token)
        {
            DataResult rst = new DataResult();
            rst.ErrorCode = SysErrorCode.KFail;
            try
            {
                var handler = new JwtSecurityTokenHandler();
                var jwtToken = handler.ReadJwtToken(token);
                var claims = jwtToken.Claims.Select(c => new { c.Type, c.Value }).ToList();
                var userAccount= claims.FirstOrDefault(p => p.Type == ClaimTypes.Sid)?.Value ?? "";
                if (_cache.TryGetValue<int>("Uo@" + userAccount, out int sysFlag))
                {
                    rst.ErrorCode = SysErrorCode.KTokenInvalid;
                }
                else if (_cache.TryGetValue<string>("Ur@" + userAccount, out string cacheToken))
                {
                    if (cacheToken.Equals(token))
                    {
                        rst.ErrorCode = SysErrorCode.KSuccess;
                    }
                    else
                    {
                        rst.ErrorCode = SysErrorCode.KTokenInvalid;
                    }
                }
            }
            catch (Exception ex)
            {
            }
            rst.Message = SysErrorCode.GetErrorMessge(rst.ErrorCode);
            return new JsonResult(rst);
        }


        protected SysAccountDto? GetUserJwtAuthInfo()
        {
            SysAccountDto? userInfo = null;
            var userItem = HttpContext.User.Claims.FirstOrDefault(p => p.Type == ClaimTypes.Sid);
            if (userItem != null)
            {
                userInfo = new SysAccountDto();
                userInfo.Account = userItem.Value;
                userInfo.Name = HttpContext.User.Claims.FirstOrDefault(p => p.Type == ClaimTypes.Name)?.Value ?? "";
                userInfo.SysFlag = ConvertHelper.ToInt32(HttpContext.User.Claims.FirstOrDefault(p => p.Type == ClaimTypes.Spn)?.Value ?? "0");
                userInfo.Action = HttpContext.User.Claims.FirstOrDefault(p => p.Type == ClaimTypes.System)?.Value ?? "";
            }
            return userInfo;

        }

        public JwtAuthorizationDto CreateToken(SysAccountDto account)
        {
            JwtAuthorizationDto jwt = null;
            DateTime authTime = DateTime.Now;
            DateTime expiresAt = authTime.AddHours(2);

            string tokenStr = CreateSecurityToken(account, authTime, expiresAt);

            //存储 Token 信息
            jwt = new JwtAuthorizationDto
            {
                Auths = string.Format("{0:yyyy-MM-dd HH:mm:ss}", authTime),
                Expires = string.Format("{0:yyyy-MM-dd HH:mm:ss}", expiresAt),
                Token = tokenStr,
            };
            return jwt;
        }

        private string CreateSecurityToken(SysAccountDto account, DateTime authTime, DateTime expiresAt)
        {
            string rtnValue = string.Empty;
            try
            {
                SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtDto.JwtKey));
                IEnumerable<Claim> claims = new Claim[] {
                    new Claim (ClaimTypes.Sid,account.Account),
                    new Claim(ClaimTypes.Name,account.Name) ,
                    new Claim(ClaimTypes.Spn,account.SysFlag.ToString()) ,
                    new Claim(ClaimTypes.System,account.Action) ,
                    new Claim (ClaimTypes.Expiration,expiresAt.ToString("yyyy-MM-dd HH:mm:ss")),
                };
                SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
                //.NET Core’s JwtSecurityToken class takes on the heavy lifting and actually creates the token.
                JwtSecurityToken token = new JwtSecurityToken(
                    //颁发者
                    issuer: jwtDto.Issuer,
                    //接收者
                    audience: jwtDto.Audience,
                    //过期时间
                    expires: expiresAt,
                    //签名证书
                    signingCredentials: creds,
                    //自定义参数
                    claims: claims
                );
                rtnValue = new JwtSecurityTokenHandler().WriteToken(token);
            }
            catch (Exception ex)
            {
               // log.LogError(ex, "<CreateJwtToken>");
            }
            return rtnValue;
        }
    }
}
